package auth

import (
	"errors"

	"gorm.io/gorm"
)

// Repository 认证仓储接口
type Repository interface {
	// User 相关
	GetUserByID(id uint) (*User, error)
	GetUserByUsername(username string) (*User, error)
	CreateUser(user *User) error
	UpdateUser(user *User) error
	DeleteUser(id uint) error
	ListUsers(query *UserQuery) ([]*User, int64, error)

	// Role 相关
	GetRoleByID(id uint) (*Role, error)
	GetRoleByCode(code string) (*Role, error)
	CreateRole(role *Role) error
	UpdateRole(role *Role) error
	DeleteRole(id uint) error
	ListRoles(query *RoleQuery) ([]*Role, int64, error)

	// Permission 相关
	GetPermissionByID(id uint) (*Permission, error)
	GetPermissionByCode(code string) (*Permission, error)
	CreatePermission(permission *Permission) error
	UpdatePermission(permission *Permission) error
	DeletePermission(id uint) error
	ListPermissions(query *PermissionQuery) ([]*Permission, int64, error)

	// 关联关系
	GetUserRoles(userID uint) ([]*Role, error)
	AssignRolesToUser(userID uint, roleIDs []uint) error
	RemoveRolesFromUser(userID uint, roleIDs []uint) error

	GetRolePermissions(roleCode string) ([]*Permission, error)
	AssignPermissionsToRole(roleCode string, permissionCodes []string, resourceCode string) error
	RemovePermissionsFromRole(roleCode string, permissionCodes []string, resourceCode string) error
}

// UserQuery 用户查询条件
type UserQuery struct {
	TenantID uint
	Username string
	Status   *int
	PageNum  int
	PageSize int
}

// RoleQuery 角色查询条件
type RoleQuery struct {
	TenantID uint
	Name     string
	Code     string
	Status   *int
	PageNum  int
	PageSize int
}

// PermissionQuery 权限查询条件
type PermissionQuery struct {
	TenantID uint
	Name     string
	Code     string
	Type     *int
	Status   *int
	PageNum  int
	PageSize int
}

// GormRepository GORM实现的认证仓储
type GormRepository struct {
	db *gorm.DB
}

// NewGormRepository 创建GORM实现的认证仓储
func NewGormRepository(db *gorm.DB) Repository {
	return &GormRepository{db: db}
}

// GetUserByID 根据ID获取用户
func (r *GormRepository) GetUserByID(id uint) (*User, error) {
	var user User
	if err := r.db.First(&user, id).Error; err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, ErrUserNotFound
		}
		return nil, err
	}
	return &user, nil
}

// GetUserByUsername 根据用户名获取用户
func (r *GormRepository) GetUserByUsername(username string) (*User, error) {
	var user User
	if err := r.db.Where("username = ?", username).First(&user).Error; err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, ErrUserNotFound
		}
		return nil, err
	}
	return &user, nil
}

// CreateUser 创建用户
func (r *GormRepository) CreateUser(user *User) error {
	// 检查用户名是否已存在
	var count int64
	if err := r.db.Model(&User{}).Where("username = ?", user.Username).Count(&count).Error; err != nil {
		return err
	}
	if count > 0 {
		return ErrUserAlreadyExists
	}
	return r.db.Create(user).Error
}

// UpdateUser 更新用户
func (r *GormRepository) UpdateUser(user *User) error {
	return r.db.Save(user).Error
}

// DeleteUser 删除用户
func (r *GormRepository) DeleteUser(id uint) error {
	return r.db.Delete(&User{}, id).Error
}

// ListUsers 查询用户列表
func (r *GormRepository) ListUsers(query *UserQuery) ([]*User, int64, error) {
	db := r.db.Model(&User{})

	// 应用查询条件
	if query.TenantID > 0 {
		db = db.Where("tenant_id = ?", query.TenantID)
	}
	if query.Username != "" {
		db = db.Where("username LIKE ?", "%"+query.Username+"%")
	}
	if query.Status != nil {
		db = db.Where("status = ?", *query.Status)
	}

	// 计算总数
	var total int64
	if err := db.Count(&total).Error; err != nil {
		return nil, 0, err
	}

	// 分页查询
	if query.PageNum > 0 && query.PageSize > 0 {
		offset := (query.PageNum - 1) * query.PageSize
		db = db.Offset(offset).Limit(query.PageSize)
	}

	// 执行查询
	var users []*User
	if err := db.Find(&users).Error; err != nil {
		return nil, 0, err
	}

	return users, total, nil
}

// GetRoleByID 根据ID获取角色
func (r *GormRepository) GetRoleByID(id uint) (*Role, error) {
	var role Role
	if err := r.db.First(&role, id).Error; err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, errors.New("role not found")
		}
		return nil, err
	}
	return &role, nil
}

// GetRoleByCode 根据编码获取角色
func (r *GormRepository) GetRoleByCode(code string) (*Role, error) {
	var role Role
	if err := r.db.Where("code = ?", code).First(&role).Error; err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, errors.New("role not found")
		}
		return nil, err
	}
	return &role, nil
}

// CreateRole 创建角色
func (r *GormRepository) CreateRole(role *Role) error {
	// 检查角色编码是否已存在
	var count int64
	if err := r.db.Model(&Role{}).Where("code = ?", role.Code).Count(&count).Error; err != nil {
		return err
	}
	if count > 0 {
		return errors.New("role code already exists")
	}
	return r.db.Create(role).Error
}

// UpdateRole 更新角色
func (r *GormRepository) UpdateRole(role *Role) error {
	return r.db.Save(role).Error
}

// DeleteRole 删除角色
func (r *GormRepository) DeleteRole(id uint) error {
	return r.db.Delete(&Role{}, id).Error
}

// ListRoles 查询角色列表
func (r *GormRepository) ListRoles(query *RoleQuery) ([]*Role, int64, error) {
	db := r.db.Model(&Role{})

	// 应用查询条件
	if query.TenantID > 0 {
		db = db.Where("tenant_id = ?", query.TenantID)
	}
	if query.Name != "" {
		db = db.Where("name LIKE ?", "%"+query.Name+"%")
	}
	if query.Code != "" {
		db = db.Where("code LIKE ?", "%"+query.Code+"%")
	}
	if query.Status != nil {
		db = db.Where("status = ?", *query.Status)
	}

	// 计算总数
	var total int64
	if err := db.Count(&total).Error; err != nil {
		return nil, 0, err
	}

	// 分页查询
	if query.PageNum > 0 && query.PageSize > 0 {
		offset := (query.PageNum - 1) * query.PageSize
		db = db.Offset(offset).Limit(query.PageSize)
	}

	// 执行查询
	var roles []*Role
	if err := db.Find(&roles).Error; err != nil {
		return nil, 0, err
	}

	return roles, total, nil
}

// GetPermissionByID 根据ID获取权限
func (r *GormRepository) GetPermissionByID(id uint) (*Permission, error) {
	var permission Permission
	if err := r.db.First(&permission, id).Error; err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, errors.New("permission not found")
		}
		return nil, err
	}
	return &permission, nil
}

// GetPermissionByCode 根据编码获取权限
func (r *GormRepository) GetPermissionByCode(code string) (*Permission, error) {
	var permission Permission
	if err := r.db.Where("code = ?", code).First(&permission).Error; err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, errors.New("permission not found")
		}
		return nil, err
	}
	return &permission, nil
}

// CreatePermission 创建权限
func (r *GormRepository) CreatePermission(permission *Permission) error {
	// 检查权限编码是否已存在
	var count int64
	if err := r.db.Model(&Permission{}).Where("code = ?", permission.Code).Count(&count).Error; err != nil {
		return err
	}
	if count > 0 {
		return errors.New("permission code already exists")
	}
	return r.db.Create(permission).Error
}

// UpdatePermission 更新权限
func (r *GormRepository) UpdatePermission(permission *Permission) error {
	return r.db.Save(permission).Error
}

// DeletePermission 删除权限
func (r *GormRepository) DeletePermission(id uint) error {
	return r.db.Delete(&Permission{}, id).Error
}

// ListPermissions 查询权限列表
func (r *GormRepository) ListPermissions(query *PermissionQuery) ([]*Permission, int64, error) {
	db := r.db.Model(&Permission{})

	// 应用查询条件
	if query.TenantID > 0 {
		db = db.Where("tenant_id = ?", query.TenantID)
	}
	if query.Name != "" {
		db = db.Where("name LIKE ?", "%"+query.Name+"%")
	}
	if query.Code != "" {
		db = db.Where("code LIKE ?", "%"+query.Code+"%")
	}
	if query.Type != nil {
		db = db.Where("type = ?", *query.Type)
	}
	if query.Status != nil {
		db = db.Where("status = ?", *query.Status)
	}

	// 计算总数
	var total int64
	if err := db.Count(&total).Error; err != nil {
		return nil, 0, err
	}

	// 分页查询
	if query.PageNum > 0 && query.PageSize > 0 {
		offset := (query.PageNum - 1) * query.PageSize
		db = db.Offset(offset).Limit(query.PageSize)
	}

	// 执行查询
	var permissions []*Permission
	if err := db.Find(&permissions).Error; err != nil {
		return nil, 0, err
	}

	return permissions, total, nil
}

// GetUserRoles 获取用户角色
func (r *GormRepository) GetUserRoles(userID uint) ([]*Role, error) {
	var roles []*Role
	err := r.db.Table("auth_role").
		Joins("JOIN auth_user_role ON auth_role.id = auth_user_role.role_id").
		Where("auth_user_role.user_id = ?", userID).
		Find(&roles).Error
	return roles, err
}

// AssignRolesToUser 为用户分配角色
func (r *GormRepository) AssignRolesToUser(userID uint, roleIDs []uint) error {
	// 开启事务
	return r.db.Transaction(func(tx *gorm.DB) error {
		// 删除现有关联
		if err := tx.Where("user_id = ?", userID).Delete(&UserRole{}).Error; err != nil {
			return err
		}

		// 创建新关联
		for _, roleID := range roleIDs {
			userRole := &UserRole{
				UserID: userID,
				RoleID: roleID,
			}
			if err := tx.Create(userRole).Error; err != nil {
				return err
			}
		}

		return nil
	})
}

// RemoveRolesFromUser 从用户移除角色
func (r *GormRepository) RemoveRolesFromUser(userID uint, roleIDs []uint) error {
	return r.db.Where("user_id = ? AND role_id IN ?", userID, roleIDs).Delete(&UserRole{}).Error
}

// GetRolePermissions 获取角色权限
func (r *GormRepository) GetRolePermissions(roleCode string) ([]*Permission, error) {
	var permissions []*Permission
	err := r.db.Table("auth_permission").
		Joins("JOIN auth_role_permission ON auth_permission.code = auth_role_permission.permission_code").
		Where("auth_role_permission.role_code = ?", roleCode).
		Find(&permissions).Error
	return permissions, err
}

// AssignPermissionsToRole 为角色分配权限
func (r *GormRepository) AssignPermissionsToRole(roleCode string, permissionCodes []string, resourceCode string) error {
	// 开启事务
	return r.db.Transaction(func(tx *gorm.DB) error {
		// 删除现有关联
		if err := tx.Where("role_code = ? AND resource_code = ?", roleCode, resourceCode).Delete(&RolePermission{}).Error; err != nil {
			return err
		}

		// 创建新关联
		for _, permCode := range permissionCodes {
			rolePerm := &RolePermission{
				RoleCode:       roleCode,
				PermissionCode: permCode,
				ResourceCode:   resourceCode,
			}
			if err := tx.Create(rolePerm).Error; err != nil {
				return err
			}
		}

		return nil
	})
}

// RemovePermissionsFromRole 从角色移除权限
func (r *GormRepository) RemovePermissionsFromRole(roleCode string, permissionCodes []string, resourceCode string) error {
	return r.db.Where("role_code = ? AND permission_code IN ? AND resource_code = ?",
		roleCode, permissionCodes, resourceCode).Delete(&RolePermission{}).Error
}
